unsigned char sysbench_sql_lua[] =
  "-- Copyright (C) 2017 Alexey Kopytov <akopytov@gmail.com>\n"
  "\n"
  "-- This program is free software; you can redistribute it and/or modify\n"
  "-- it under the terms of the GNU General Public License as published by\n"
  "-- the Free Software Foundation; either version 2 of the License, or\n"
  "-- (at your option) any later version.\n"
  "\n"
  "-- This program is distributed in the hope that it will be useful,\n"
  "-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
  "-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
  "-- GNU General Public License for more details.\n"
  "\n"
  "-- You should have received a copy of the GNU General Public License\n"
  "-- along with this program; if not, write to the Free Software\n"
  "-- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
  "\n"
  "-- ----------------------------------------------------------------------\n"
  "-- SQL API\n"
  "-- ----------------------------------------------------------------------\n"
  "\n"
  "ffi = require(\"ffi\")\n"
  "\n"
  "sysbench.sql = {}\n"
  "\n"
  "ffi.cdef[[\n"
  "/*\n"
  "  The following definitions have been copied with modifications from db_driver.h\n"
  "*/\n"
  "\n"
  "typedef enum\n"
  "{\n"
  "  DB_ERROR_NONE,                /* no error(s) */\n"
  "  DB_ERROR_IGNORABLE,           /* error should be ignored as defined by command\n"
  "                                line arguments or a custom error handler */\n"
  "  DB_ERROR_FATAL                /* non-ignorable error */\n"
  "} sql_error_t;\n"
  "\n"
  "typedef struct\n"
  "{\n"
  "  const char      *sname;    /* short name */\n"
  "  const char      *lname;    /* long name */\n"
  "\n"
  "  const char      opaque[?];\n"
  "} sql_driver;\n"
  "\n"
  "typedef struct {\n"
  "  uint32_t        len;         /* Value length */\n"
  "  const char      *ptr;        /* Value string */\n"
  "} sql_value;\n"
  "\n"
  "/* Result set row definition */\n"
  "\n"
  "typedef struct\n"
  "{\n"
  "  void            *ptr;        /* Driver-specific row data */\n"
  "  sql_value       *values;     /* Array of column values */\n"
  "} sql_row;\n"
  "\n"
  "/* Statistic counter types */\n"
  "\n"
  "typedef enum\n"
  "{\n"
  "  SB_CNT_OTHER,\n"
  "  SB_CNT_READ,\n"
  "  SB_CNT_WRITE,\n"
  "  SB_CNT_TRX,\n"
  "  SB_CNT_ERROR,\n"
  "  SB_CNT_RECONNECT,\n"
  "  SB_CNT_BYTES_READ,\n"
  "  SB_CNT_BYTES_WRITTEN,\n"
  "  SB_CNT_MAX\n"
  "} sb_counter_type;\n"
  "\n"
  "typedef struct\n"
  "{\n"
  "  sql_error_t     error;             /* Driver-independent error code */\n"
  "  int             sql_errno;         /* Driver-specific error code */\n"
  "  const char      *sql_state;        /* Database-specific SQL state */\n"
  "  const char      *sql_errmsg;       /* Database-specific error message */\n"
  "  sql_driver      *driver;           /* DB driver for this connection */\n"
  "\n"
  "  const char      opaque[?];\n"
  "} sql_connection;\n"
  "\n"
  "typedef struct\n"
  "{\n"
  "  sql_connection  *connection;\n"
  "\n"
  "  const char      opaque[?];\n"
  "} sql_statement;\n"
  "\n"
  "/* Result set definition */\n"
  "\n"
  "typedef struct\n"
  "{\n"
  "  sb_counter_type   counter;     /* Statistical counter type */\n"
  "  uint32_t       nrows;         /* Number of affected rows */\n"
  "  uint32_t       nfields;       /* Number of fields */\n"
  "  sql_statement  *statement;    /* Pointer to prepared statement (if used) */\n"
  "  void           *ptr;          /* Pointer to driver-specific data */\n"
  "  sql_row        row;           /* Last fetched row */\n"
  "} sql_result;\n"
  "\n"
  "typedef enum\n"
  "{\n"
  "  SQL_TYPE_NONE,\n"
  "  SQL_TYPE_TINYINT,\n"
  "  SQL_TYPE_SMALLINT,\n"
  "  SQL_TYPE_INT,\n"
  "  SQL_TYPE_BIGINT,\n"
  "  SQL_TYPE_FLOAT,\n"
  "  SQL_TYPE_DOUBLE,\n"
  "  SQL_TYPE_TIME,\n"
  "  SQL_TYPE_DATE,\n"
  "  SQL_TYPE_DATETIME,\n"
  "  SQL_TYPE_TIMESTAMP,\n"
  "  SQL_TYPE_CHAR,\n"
  "  SQL_TYPE_VARCHAR\n"
  "} sql_bind_type_t;\n"
  "\n"
  "typedef struct\n"
  "{\n"
  "  sql_bind_type_t   type;\n"
  "  void             *buffer;\n"
  "  unsigned long    *data_len;\n"
  "  unsigned long    max_len;\n"
  "  char             *is_null;\n"
  "} sql_bind;\n"
  "\n"
  "sql_driver *db_create(const char *);\n"
  "int db_destroy(sql_driver *drv);\n"
  "\n"
  "sql_connection *db_connection_create(sql_driver * drv);\n"
  "int db_connection_close(sql_connection *con);\n"
  "int db_connection_reconnect(sql_connection *con);\n"
  "void db_connection_free(sql_connection *con);\n"
  "\n"
  "int db_bulk_insert_init(sql_connection *, const char *, size_t);\n"
  "int db_bulk_insert_next(sql_connection *, const char *, size_t);\n"
  "int db_bulk_insert_done(sql_connection *);\n"
  "\n"
  "sql_result *db_query(sql_connection *con, const char *query, size_t len);\n"
  "\n"
  "sql_row *db_fetch_row(sql_result *rs);\n"
  "\n"
  "sql_statement *db_prepare(sql_connection *con, const char *query, size_t len);\n"
  "int db_bind_param(sql_statement *stmt, sql_bind *params, size_t len);\n"
  "int db_bind_result(sql_statement *stmt, sql_bind *results, size_t len);\n"
  "sql_result *db_execute(sql_statement *stmt);\n"
  "int db_close(sql_statement *stmt);\n"
  "\n"
  "int db_free_results(sql_result *);\n"
  "]]\n"
  "\n"
  "local sql_driver = ffi.typeof('sql_driver *')\n"
  "local sql_connection = ffi.typeof('sql_connection *')\n"
  "local sql_statement = ffi.typeof('sql_statement *')\n"
  "local sql_bind = ffi.typeof('sql_bind');\n"
  "local sql_result = ffi.typeof('sql_result');\n"
  "local sql_value = ffi.typeof('sql_value');\n"
  "local sql_row = ffi.typeof('sql_row');\n"
  "\n"
  "sysbench.sql.type =\n"
  "{\n"
  "      NONE = ffi.C.SQL_TYPE_NONE,\n"
  "      TINYINT = ffi.C.SQL_TYPE_TINYINT,\n"
  "      SMALLINT = ffi.C.SQL_TYPE_SMALLINT,\n"
  "      INT = ffi.C.SQL_TYPE_INT,\n"
  "      BIGINT = ffi.C.SQL_TYPE_BIGINT,\n"
  "      FLOAT = ffi.C.SQL_TYPE_FLOAT,\n"
  "      DOUBLE = ffi.C.SQL_TYPE_DOUBLE,\n"
  "      TIME = ffi.C.SQL_TYPE_TIME,\n"
  "      DATE = ffi.C.SQL_TYPE_DATE,\n"
  "      DATETIME = ffi.C.SQL_TYPE_DATETIME,\n"
  "      TIMESTAMP = ffi.C.SQL_TYPE_TIMESTAMP,\n"
  "      CHAR = ffi.C.SQL_TYPE_CHAR,\n"
  "      VARCHAR = ffi.C.SQL_TYPE_VARCHAR\n"
  "   }\n"
  "\n"
  "-- Initialize a given SQL driver and return a handle to it to create\n"
  "-- connections. A nil driver name (i.e. no function argument) initializes the\n"
  "-- default driver, i.e. the one specified with --db-driver on the command line.\n"
  "function sysbench.sql.driver(driver_name)\n"
  "   local drv = ffi.C.db_create(driver_name)\n"
  "   if (drv == nil) then\n"
  "      error(\"failed to initialize the DB driver\", 2)\n"
  "   end\n"
  "   return ffi.gc(drv, ffi.C.db_destroy)\n"
  "end\n"
  "\n"
  "-- sql_driver methods\n"
  "local driver_methods = {}\n"
  "\n"
  "function driver_methods.connect(self)\n"
  "   local con = ffi.C.db_connection_create(self)\n"
  "   if con == nil then\n"
  "      error(\"connection creation failed\", 2)\n"
  "   end\n"
  "   return ffi.gc(con, ffi.C.db_connection_free)\n"
  "end\n"
  "\n"
  "function driver_methods.name(self)\n"
  "   return ffi.string(self.sname)\n"
  "end\n"
  "\n"
  "-- sql_driver metatable\n"
  "local driver_mt = {\n"
  "   __index = driver_methods,\n"
  "   __gc = ffi.C.db_destroy,\n"
  "   __tostring = function() return '<sql_driver>' end,\n"
  "}\n"
  "ffi.metatype(\"sql_driver\", driver_mt)\n"
  "\n"
  "-- sql_connection methods\n"
  "local connection_methods = {}\n"
  "\n"
  "function connection_methods.disconnect(self)\n"
  "   return assert(ffi.C.db_connection_close(self) == 0)\n"
  "end\n"
  "\n"
  "function connection_methods.reconnect(self)\n"
  "   return assert(ffi.C.db_connection_reconnect(self) == 0)\n"
  "end\n"
  "\n"
  "function connection_methods.check_error(self, rs, query)\n"
  "   if rs ~= nil or self.error == sysbench.sql.error.NONE then\n"
  "      return rs\n"
  "   end\n"
  "\n"
  "   if self.sql_state == nil or self.sql_errmsg == nil then\n"
  "      -- It must be an API error, don't bother trying to downgrade it an\n"
  "      -- ignorable error\n"
  "      error(\"SQL API error\", 3)\n"
  "   end\n"
  "\n"
  "   local sql_state = ffi.string(self.sql_state)\n"
  "   local sql_errmsg = ffi.string(self.sql_errmsg)\n"
  "\n"
  "   -- Create an error descriptor containing connection, failed query, SQL error\n"
  "   -- number, state and error message provided by the SQL driver\n"
  "   errdesc = {\n"
  "      connection = self,\n"
  "      query = query,\n"
  "      sql_errno = self.sql_errno,\n"
  "      sql_state = sql_state,\n"
  "      sql_errmsg = sql_errmsg\n"
  "   }\n"
  "\n"
  "   -- Check if the error has already been marked as ignorable by the driver, or\n"
  "   -- there is an error hook that allows downgrading it to IGNORABLE\n"
  "   if (self.error == sysbench.sql.error.FATAL and\n"
  "          type(sysbench.hooks.sql_error_ignorable) == \"function\" and\n"
  "          sysbench.hooks.sql_error_ignorable(errdesc)) or\n"
  "      self.error == sysbench.sql.error.IGNORABLE\n"
  "   then\n"
  "      -- Throw a 'restart event' exception that can be caught by the user script\n"
  "      -- to do some extra steps to restart a transaction (e.g. reprepare\n"
  "      -- statements after a reconnect). Otherwise it will be caught by\n"
  "      -- thread_run() in sysbench.lua, in which case the entire current event\n"
  "      -- will be restarted without extra processing.\n"
  "      errdesc.errcode = sysbench.error.RESTART_EVENT\n"
  "      error(errdesc, 3)\n"
  "   end\n"
  "\n"
  "   -- Just throw a regular error message on a fatal error\n"
  "   error(string.format(\"SQL error, errno = %d, state = '%s': %s\",\n"
  "                       self.sql_errno, sql_state, sql_errmsg), 2)\n"
  "end\n"
  "\n"
  "function connection_methods.query(self, query)\n"
  "   local rs = ffi.C.db_query(self, query, #query)\n"
  "   return self:check_error(rs, query)\n"
  "end\n"
  "\n"
  "function connection_methods.bulk_insert_init(self, query)\n"
  "   return assert(ffi.C.db_bulk_insert_init(self, query, #query) == 0,\n"
  "                 \"db_bulk_insert_init() failed\")\n"
  "end\n"
  "\n"
  "function connection_methods.bulk_insert_next(self, val)\n"
  "   return assert(ffi.C.db_bulk_insert_next(self, val, #val) == 0,\n"
  "                 \"db_bulk_insert_next() failed\")\n"
  "end\n"
  "\n"
  "function connection_methods.bulk_insert_done(self)\n"
  "   return assert(ffi.C.db_bulk_insert_done(self) == 0,\n"
  "                 \"db_bulk_insert_done() failed\")\n"
  "end\n"
  "\n"
  "function connection_methods.prepare(self, query)\n"
  "   local stmt = ffi.C.db_prepare(self, query, #query)\n"
  "   if stmt == nil then\n"
  "      self:check_error(nil, query)\n"
  "   end\n"
  "   return stmt\n"
  "end\n"
  "\n"
  "-- A convenience wrapper around sql_connection:query() and\n"
  "-- sql_result:fetch_row(). Executes the specified query and returns the first\n"
  "-- row from the result set, if available, or nil otherwise\n"
  "function connection_methods.query_row(self, query)\n"
  "   local rs = self:query(query)\n"
  "\n"
  "   if rs == nil or rs.nrows == 0 then\n"
  "      return nil\n"
  "   end\n"
  "\n"
  "   return unpack(rs:fetch_row(), 1, rs.nfields)\n"
  "end\n"
  "\n"
  "-- sql_connection metatable\n"
  "local connection_mt = {\n"
  "   __index = connection_methods,\n"
  "   __tostring = function() return '<sql_connection>' end,\n"
  "   __gc = ffi.C.db_connection_free,\n"
  "}\n"
  "ffi.metatype(\"sql_connection\", connection_mt)\n"
  "\n"
  "-- sql_param\n"
  "local sql_param = {}\n"
  "function sql_param.set(self, value)\n"
  "   local sql_type = sysbench.sql.type\n"
  "   local btype = self.type\n"
  "\n"
  "   if (value == nil) then\n"
  "      self.is_null[0] = true\n"
  "      return\n"
  "   end\n"
  "\n"
  "   self.is_null[0] = false\n"
  "\n"
  "   if btype == sql_type.TINYINT or\n"
  "      btype == sql_type.SMALLINT or\n"
  "      btype == sql_type.INT or\n"
  "      btype == sql_type.BIGINT\n"
  "   then\n"
  "      self.buffer[0] = value\n"
  "   elseif btype == sql_type.FLOAT or\n"
  "      btype == sql_type.DOUBLE\n"
  "   then\n"
  "      self.buffer[1] = value\n"
  "   elseif btype == sql_type.CHAR or\n"
  "      btype == sql_type.VARCHAR\n"
  "   then\n"
  "      local len = #value\n"
  "      len = self.max_len < len and self.max_len or len\n"
  "      ffi.copy(self.buffer, value, len)\n"
  "      self.data_len[0] = len\n"
  "   else\n"
  "      error(\"Unsupported argument type: \" .. btype, 2)\n"
  "   end\n"
  "end\n"
  "\n"
  "function sql_param.set_rand_str(self, fmt)\n"
  "   local sql_type = sysbench.sql.type\n"
  "   local btype = self.type\n"
  "\n"
  "   self.is_null[0] = false\n"
  "\n"
  "   if btype == sql_type.CHAR or\n"
  "      btype == sql_type.VARCHAR\n"
  "   then\n"
  "      local len = #fmt\n"
  "      len = self.max_len < len and self.max_len or len\n"
  "      ffi.C.sb_rand_str(fmt, self.buffer)\n"
  "      self.data_len[0] = len\n"
  "   else\n"
  "      error(\"Unsupported argument type: \" .. btype, 2)\n"
  "   end\n"
  "end\n"
  "\n"
  "sql_param.__index = sql_param\n"
  "sql_param.__tostring = function () return '<sql_param>' end\n"
  "\n"
  "-- sql_statement methods\n"
  "local statement_methods = {}\n"
  "\n"
  "function statement_methods.bind_create(self, btype, max_len)\n"
  "   local sql_type = sysbench.sql.type\n"
  "\n"
  "   local param = setmetatable({}, sql_param)\n"
  "\n"
  "   if btype == sql_type.TINYINT or\n"
  "      btype == sql_type.SMALLINT or\n"
  "      btype == sql_type.INT or\n"
  "      btype == sql_type.BIGINT\n"
  "   then\n"
  "      param.type = sql_type.BIGINT\n"
  "      param.buffer = ffi.new('int64_t[1]')\n"
  "      param.max_len = 8\n"
  "   elseif btype == sql_type.FLOAT or\n"
  "      btype == sql_type.DOUBLE\n"
  "   then\n"
  "      param.type = sql_type.DOUBLE\n"
  "      param.buffer = ffi.new('double[1]')\n"
  "      param.max_len = 8\n"
  "   elseif btype == sql_type.CHAR or\n"
  "      btype == sql_type.VARCHAR\n"
  "   then\n"
  "      param.type = sql_type.VARCHAR\n"
  "      param.buffer = ffi.new('char[?]', max_len)\n"
  "      param.max_len = max_len\n"
  "   else\n"
  "      error(\"Unsupported argument type: \" .. btype, 2)\n"
  "   end\n"
  "\n"
  "   param.data_len = ffi.new('unsigned long[1]')\n"
  "   param.is_null = ffi.new('char[1]')\n"
  "\n"
  "   return param\n"
  "end\n"
  "\n"
  "function statement_methods.bind_param(self, ...)\n"
  "   local len = select('#', ...)\n"
  "   if len  < 1 then return nil end\n"
  "\n"
  "   local binds = ffi.new(\"sql_bind[?]\", len)\n"
  "\n"
  "   local i, param\n"
  "\n"
  "   for i, param in ipairs({...}) do\n"
  "      binds[i-1].type = param.type\n"
  "      binds[i-1].buffer = param.buffer\n"
  "      binds[i-1].data_len = param.data_len\n"
  "      binds[i-1].max_len = param.max_len\n"
  "      binds[i-1].is_null = param.is_null\n"
  "   end\n"
  "   return ffi.C.db_bind_param(self, binds, len)\n"
  "end\n"
  "\n"
  "function statement_methods.execute(self)\n"
  "   local rs = ffi.C.db_execute(self)\n"
  "   return self.connection:check_error(rs, '<prepared statement>')\n"
  "end\n"
  "\n"
  "function statement_methods.close(self)\n"
  "   return ffi.C.db_close(self)\n"
  "end\n"
  "\n"
  "-- sql_statement metatable\n"
  "local statement_mt = {\n"
  "   __index = statement_methods,\n"
  "   __tostring = function() return '<sql_statement>' end,\n"
  "   __gc = ffi.C.db_close,\n"
  "}\n"
  "ffi.metatype(\"sql_statement\", statement_mt)\n"
  "\n"
  "local bind_mt = {\n"
  "   __tostring = function() return '<sql_bind>' end,\n"
  "}\n"
  "ffi.metatype(\"sql_bind\", bind_mt)\n"
  "\n"
  "-- sql_result methods\n"
  "local result_methods = {}\n"
  "\n"
  "-- Returns the next row of values from a result set, or nil if there are no more\n"
  "-- rows to fetch. Values are returned as an array, i.e. a table with numeric\n"
  "-- indexes starting from 1. The total number of values (i.e. fields in a result\n"
  "-- set) can be obtained from sql_result.nfields.\n"
  "function result_methods.fetch_row(self)\n"
  "   local res = {}\n"
  "   local row = ffi.C.db_fetch_row(self)\n"
  "\n"
  "   if row == nil then\n"
  "      return nil\n"
  "   end\n"
  "\n"
  "   local i\n"
  "   for i = 0, self.nfields-1 do\n"
  "      if row.values[i].ptr ~= nil then -- not a NULL value\n"
  "         res[i+1] = ffi.string(row.values[i].ptr, tonumber(row.values[i].len))\n"
  "      end\n"
  "   end\n"
  "\n"
  "   return res\n"
  "end\n"
  "\n"
  "function result_methods.free(self)\n"
  "   return assert(ffi.C.db_free_results(self) == 0, \"db_free_results() failed\")\n"
  "end\n"
  "\n"
  "-- sql_results metatable\n"
  "local result_mt = {\n"
  "   __index = result_methods,\n"
  "   __tostring = function() return '<sql_result>' end,\n"
  "   __gc = ffi.C.db_free_results\n"
  "}\n"
  "ffi.metatype(\"sql_result\", result_mt)\n"
  "\n"
  "-- error codes\n"
  "sysbench.sql.error = {}\n"
  "sysbench.sql.error.NONE = ffi.C.DB_ERROR_NONE\n"
  "sysbench.sql.error.IGNORABLE = ffi.C.DB_ERROR_IGNORABLE\n"
  "sysbench.sql.error.FATAL = ffi.C.DB_ERROR_FATAL\n"
;
size_t sysbench_sql_lua_len = sizeof(sysbench_sql_lua) - 1;
